package com.chats.websocket;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.chats.pojo.Message;
import com.chats.pojo.User;
import com.chats.pojo.vo.MessageVO;
import com.chats.pojo.vo.UserVO;
import com.chats.service.MessageService;
import com.chats.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * WebSocket服务
 */
@Component
@Slf4j
@ServerEndpoint("/socket/{id}")
public class WebSocketServer {

    public static final Map<Long, Session> sessionMap = new ConcurrentHashMap<>(); // 存储对象map

    public static final List<UserVO> userList = new CopyOnWriteArrayList<>(); // 用户集合

    private UserVO user; // 当前用户

    private Session session; // 当前客户端session

    private static UserService userService;

    private static MessageService messageService;

    @Autowired
    public void setUserService(UserService userService) {
        WebSocketServer.userService = userService;
    }

    @Autowired
    public void setMessageService(MessageService messageService) {
        WebSocketServer.messageService = messageService;
    }

    /***
     * WebSocket 建立连接事件
     * 1.把登录的用户存到 sessionMap 中
     * 2.发送给所有人当前登录人员信息
     * 3.发送sessionId给对应用户
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("id") Long id) {
        log.info("id为{}进入聊天室", id);
        // 建立连接
        this.session = session;
        sessionMap.put(id, session);
        // 查询用户
        User user = userService.getById(id);
        log.info("用户：{}", user);
        this.user = new UserVO(user);

        userList.add(this.user);

        // 发送在线列表
        sendUserList();
        // 发送sessionId
        sendSessionId();
        showUserList();
    }

    /**
     * WebSocket 关闭连接事件
     * 1.把登出的用户从 sessionMap 中剃除
     * 2.发送给所有人当前登录人员信息
     */
    @OnClose
    public void onClose(@PathParam("id") Long id) {
        log.info("id为{}的用户退出了聊天室", id);
        sessionMap.remove(id);
        userList.remove(this.user);
        log.info("当前在线人数为：{}", userList.size());
        sendUserList();
        showUserList();
    }

    /**
     * WebSocket 接受信息事件
     * 接收处理客户端发来的数据
     *
     * @param message 信息
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("用户{}发送消息：{}", this.user.getUsername(), message);
        Message msg = JSON.parseObject(message, Message.class);
        // 保存消息
        messageService.save(msg);
        // 将Message转换成MessageVO
        User fromUser = userService.getById(msg.getFromId());
        MessageVO messageVO = new MessageVO(msg, fromUser.getUsername());
        String json = JSON.toJSONString(messageVO);
        Long toId = messageVO.getToId();
        log.info("toId:{}", toId);
        if(toId == 0) { // 群发
            log.info("用户{}群发消息{}", this.user.getUsername(), json);
            sendAllMessage(json);
        } else { // 单发
            log.info("用户{}发送消息给用户{}(id)，内容为:{}", this.user.getUsername(), toId, message);
            sendMessageByUser(json, sessionMap.get(toId));
        }

    }

    /**
     * WebSocket 错误事件
     *
     * @param session 用户 Session
     * @param error   错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 显示在线用户列表
     */
    private void showUserList() {
        for (UserVO user : userList) {
            log.info(user.getUsername());
        }
    }


    /**
     * 发送在线用户列表
     */
    private void sendUserList() {
        JSONObject users = new JSONObject();
        users.put("userList", userList);
        sendAllMessage(JSON.toJSONString(users));
    }

    /**
     * 给当前用户发送sessionId
     */
    private void sendSessionId() {
        try {
            JSONObject sessionId = new JSONObject();
            int value = Integer.parseInt(this.session.getId(), 16);
            sessionId.put("sessionId", String.valueOf(value));
            this.session.getBasicRemote().sendText(JSON.toJSONString(sessionId));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 当前客户端发送消息给指定用户
     *
     * @param message
     */
    public static void sendMessageByUser(String message, Session toSession) {
        try {
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("推送消息到指定用户发生错误");
            throw new RuntimeException(e);
        }
    }

    /**
     * 发送消息给所有用户
     *
     * @param message 消息
     */
    private static void sendAllMessage(String message) {
        try {
            for (Session session : sessionMap.values()) {
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            log.error("群发消息失败");
            e.printStackTrace();
        }
    }


}
